package gis

import (
	"bufio"
	"dgo/goutils/utils"
	"encoding/binary"
	"fmt"
	"io"
	"math"
	"os"
)

/*
使用格网点内插计算值
*/
type GisInterposeV3 struct {
	cnt          int
	datatag      int32
	invalidValue float64

	gridOpt *TGisGridV3
	xFieldN int
	yFieldN int

	lst []float32
}

func NewGisInterposV3() *GisInterposeV3 {
	rval := &GisInterposeV3{}
	return rval
}

func (this *GisInterposeV3) InvalidVal() float64 {
	return this.invalidValue
}

func (this *GisInterposeV3) Count() int {
	return this.cnt
}

func (this *GisInterposeV3) Status() string {
	opt := this.gridOpt
	lst := this.lst
	if lst == nil || opt == nil {
		return "NULL"
	}
	return fmt.Sprintf("cnt=%d;startlng=%.4f;startlat:%.4f;gridlng=%.4f;gridlat=%.4f", len(lst), opt.StartLng, opt.StartLat, opt.GridLng, opt.GridLat)
}

func (this *GisInterposeV3) FindBaseItemByBL(b, l float64) (h float64) {
	return this.findBaseItemByBL(this.gridOpt, b, l)
}

// 矩阵定位
func (this *GisInterposeV3) findBaseItemByBL(opt *TGisGridV3, b, l float64) (h float64) {

	x := int(math.Round((l - opt.StartLng) / opt.GridLng))
	y := int(math.Round((b - opt.StartLat) / opt.GridLat))
	idx := (y * this.xFieldN) + x
	if idx >= len(this.lst) {
		return this.invalidValue
	}
	if idx < 0 {
		return this.invalidValue
	}
	return float64(this.lst[idx])
}

func (this *GisInterposeV3) GetValue(b, l float64, sb *utils.BytesBuilder) (ok bool, v float64) {
	lst := this.lst
	gridOpt := this.gridOpt
	if lst == nil {
		return
	}
	plst := gridOpt.FindNearest4Grid(b, l)
	return this.InnerGetValue(plst, l, b, sb)
}

func (this *GisInterposeV3) InnerGetValue(gridlst [4]BLPoint, l, b float64, sb *utils.BytesBuilder) (ok bool, v1 float64) {

	opt := this.gridOpt

	t0, t1, f0, f1 := gridlst[0], gridlst[1], gridlst[2], gridlst[3]
	lst1 := make([]float64, 0, 4)
	top0_rec := this.findBaseItemByBL(opt, t0.B, t0.L)
	top1_rec := this.findBaseItemByBL(opt, t1.B, t1.L)
	flr0_rec := this.findBaseItemByBL(opt, f0.B, f0.L)
	flr1_rec := this.findBaseItemByBL(opt, f1.B, f1.L)

	if top0_rec != this.invalidValue {
		lst1 = append(lst1, t0.B, t0.L, top0_rec)
	}
	if top1_rec != this.invalidValue {
		lst1 = append(lst1, t1.B, t1.L, top1_rec)
	}
	if flr0_rec != this.invalidValue {
		lst1 = append(lst1, f0.B, f0.L, flr0_rec)
	}
	if flr1_rec != this.invalidValue {
		lst1 = append(lst1, f1.B, f1.L, flr1_rec)
	}

	if len(lst1) > 0 {

		// 距离集合
		dlst := make([]float64, len(lst1))
		var sum_1_di float64
		for i := 0; i < len(lst1); i += 3 {
			b0 := lst1[i]
			l0 := lst1[i+1]

			dlst[i] = CalcuDis(l, b, l0, b0) // 必须为正数
			if dlst[i] == 0 {
				dlst[i] = 0.00001 // 如果为0, 其实可以直接取偏差,
			}
			sum_1_di += 1 / dlst[i]

		}

		// 权重, 距离越近，权重越高, 如果不够平滑 可以加上平方
		wlst := make([]float64, len(lst1))
		for i := 0; i < len(dlst); i += 3 {
			wlst[i] = (1 / dlst[i]) / sum_1_di
		}

		var dv float64
		for i := 0; i < len(lst1); i += 3 {
			b0 := lst1[i]
			l0 := lst1[i+1]
			v0 := lst1[i+2]
			dv += wlst[i] * v0
			if sb != nil {
				sb.Appendf("格网点:[%.4f-%.4f],值:%.2f, 权重:%.2f,距离:%.2f, 计算值:%.4f\r\n", l0, b0, v0, wlst[i], dlst[i], dv)
			}
		}

		// 保留4位小数
		v1 = dv
		ok = true
	}
	return
}

func (this *GisInterposeV3) CheckReloadFromReader(br io.Reader) (cnt int, err error) {

	opt := &TGisGridV3{}
	var datatag, invalidn, xnbl, xnh, startx, starty, spanx, spany, v32 uint32
	var xfieldn, yfieldn uint16
	var v8 byte

	buf4 := make([]byte, 4)

	var vlst []float32

	var readU8 = func() (byte, error) {
		err := utils.ReadLBytes(br, buf4, 1)
		if err != nil {
			return 0, err
		}

		return buf4[0], nil
	}

	var readUint16 = func() (uint16, error) {
		err := utils.ReadLBytes(br, buf4, 2)
		if err != nil {
			return 0, err
		}

		return binary.BigEndian.Uint16(buf4), nil
	}

	var readUint32 = func() (uint32, error) {
		err := utils.ReadLBytes(br, buf4, 4)
		if err != nil {
			return 0, err
		}

		return binary.BigEndian.Uint32(buf4), nil
	}

	for {
		v8, err = readU8()
		if err == io.EOF {
			break
		}

		if v8 == 0x0D {
			datatag, err = readUint32()
			xnbl, err = readUint32()
			xnh, err = readUint32()
			invalidn, err = readUint32()
			xfieldn, err = readUint16()
			yfieldn, err = readUint16()
			startx, err = readUint32()
			spanx, err = readUint32()
			starty, err = readUint32()
			spany, err = readUint32()
			if err != nil {
				break
			}
			vlst = make([]float32, 0, xfieldn*yfieldn)
			opt.StartLng = float64(startx) / float64(xnbl)
			opt.StartLat = float64(starty) / float64(xnbl)
			opt.GridLng = float64(spanx) / float64(xnbl)
			opt.GridLat = float64(spany) / float64(xnbl)
		} else if v8 == 0x10 {
			for j := uint16(0); j < xfieldn; j++ {
				v32, err = readUint32()
				if err != nil {
					break
				}
				vlst = append(vlst, float32(int32(v32))/float32(xnh))
			}
			if err != nil {
				break
			}
			cnt++
		}
	}

	sumN := int(xfieldn) * int(yfieldn)
	if sumN != len(vlst) {
		if err != nil {
			return
		}
		return 0, fmt.Errorf("非法的数据, 数据缺失")
	}
	this.datatag = int32(datatag)
	this.invalidValue = float64(int32(invalidn))
	this.gridOpt = opt
	this.xFieldN = int(xfieldn)
	this.yFieldN = int(yfieldn)
	this.lst = vlst
	this.cnt = cnt
	err = nil
	return
}

// 0x0D, datatag(4), xn_bl(4), xn_h(4), invalidv(4), xfield-n(2), yfield-n(2), start_x(4), x_span(4), start_y(4), y_span(4)
// 0x10
func (this *GisInterposeV3) CheckReloadFromFile(fileName string) (cnt int, err error) {
	var readf *os.File

	readf, err = os.Open(fileName)
	if err != nil {
		return 0, err
	}
	defer readf.Close()
	br := bufio.NewReaderSize(readf, 4097)

	return this.CheckReloadFromReader(br)
}
